package com.wwj.study.spring.formework.webmvc.servlet;

import com.wwj.study.spring.demo.controller.TestController;
import com.wwj.study.spring.formework.annotion.WController;
import com.wwj.study.spring.formework.annotion.WRequestMapping;
import com.wwj.study.spring.formework.annotion.WRequestParam;
import com.wwj.study.spring.formework.aop.WAopProxyUtils;
import com.wwj.study.spring.formework.context.WApplicationContext;
import com.wwj.study.spring.formework.core.WBeanFactory;
import com.wwj.study.spring.formework.webmvc.WHandleAdapter;
import com.wwj.study.spring.formework.webmvc.WHandleMapping;
import com.wwj.study.spring.formework.webmvc.WModelAndView;
import com.wwj.study.spring.formework.webmvc.WViewResolver;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: wuweijun
 * @Date: 2018/12/1 20:14
 */
public class WDispatcherServlet extends HttpServlet {

    //配置文件路径名称
    private static final String LOCAL_CONFIG = "wwj-contextConfigLocation";

    //HandleMapping集合，通过url找到对应的handleMapping
    private List<WHandleMapping> handleMappings = new ArrayList<WHandleMapping>();

    //HandleAdapter集合，通过handlerMapping获取adapter
    private Map<WHandleMapping, WHandleAdapter> handleAdapterMap = new HashMap<WHandleMapping, WHandleAdapter>();

    //view集合，初始化所有页面名称+文件路径
    private List<WViewResolver> viewResolvers = new ArrayList<WViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            doDispatcher(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("<font size='25' color='blue'>500 Exception</font><br/>Details:<br/>" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]","")
                    .replaceAll("\\s","\r\n") +  "<font color='green'><i>Copyright@wwj</i></font>");
            e.printStackTrace();
        }
    }

    //请求执行
    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1,url获取handlerMapping
        //2,根据handlerMaping获取handlerAdapter
        //3.执行adapter返回modelandView
        //4.执行视图转换
        WHandleMapping handleMapping = getHandlerMapping(req);
        if (handleMapping == null) {
            String message= new String (("404 没有找到该页面").getBytes("utf-8"),"utf-8");
            resp.getWriter().write(message);
            return;
        }

        //找到adapter适配器
        WHandleAdapter adapter = this.handleAdapterMap.get(handleMapping);
        //Controller方法返回modelAndView
        //adapter执行方法
        WModelAndView modelAndView = adapter.handle(req, resp, handleMapping);

        //根据modelAndView组装view视图文件
        processDispatchResult(modelAndView, resp);
    }

    //modelAndView 写入 resp
    private void processDispatchResult(WModelAndView modelAndView, HttpServletResponse resp) throws Exception {
        if (modelAndView == null) {
            return;
        }

        if (this.viewResolvers.isEmpty()) {
            return;
        }

        for (WViewResolver v : this.viewResolvers) {
            if (!v.getName().equals(modelAndView.getViewName())) {
                continue;
            }

            //执行转换 modelAndView --> view
            String result = v.viewResolver(modelAndView);
            if (result != null) {
                resp.getWriter().write(result);
                break;
            }
        }

    }

    //根据url请求，获取对应的handlerMapping
    private WHandleMapping getHandlerMapping(HttpServletRequest req) {
        if (handleMappings.isEmpty()) {
            return null;
        }

        //获取当前请求url
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replace("/+", "/");

        //找到对应的handlerMapping
        for (WHandleMapping handleMapping : handleMappings) {
            Pattern pattern = handleMapping.getPattern();
            Matcher matcher = pattern.matcher(url);
            if (!matcher.matches()) {
                continue;
            }
            return handleMapping;
        }

        return null;
    }


    @Override
    public void init(ServletConfig config) throws ServletException {
        //初始化IOC容器
        WApplicationContext applicationContext = new WApplicationContext(config.getInitParameter(LOCAL_CONFIG));
        System.out.println("初始化IOC容器成功");
        //初始化9大组件
        initStrategies(applicationContext);
        System.out.println("初始化9大组件成功");
    }

    private void initStrategies(WApplicationContext context) {
        // 有九种策略
        // 针对于每个用户请求，都会经过一些处理的策略之后，最终才能有结果输出
        // 每种策略可以自定义干预，但是最终的结果都是一致
        // ModelAndView

        // =============  这里说的就是传说中的九大组件 ================
        initMultipartResolver(context);//文件上传解析，如果请求类型是multipart将通过MultipartResolver进行文件上传解析
        initLocaleResolver(context);//本地化解析
        initThemeResolver(context);//主题解析
        // 用来保存Controller中配置的RequestMapping和Method的一个对应关系
        initHandlerMappings(context);//通过HandlerMapping，将请求映射到处理器
        //HandlerAdapters 用来动态匹配Method参数，包括类转换，动态赋值
        initHandlerAdapters(context);//通过HandlerAdapter进行多类型的参数动态匹配
        initHandlerExceptionResolvers(context);//如果执行过程中遇到异常，将交给HandlerExceptionResolver来解析
        initRequestToViewNameTranslator(context);//直接解析请求到视图名
        //通过ViewResolvers实现动态模板的解析
        //自己解析一套模板语言
        initViewResolvers(context);//通过viewResolver解析逻辑视图到具体视图实现
        initFlashMapManager(context);//flash映射管理器
    }

    //初始化HandlerMapping 通过url找到对应的controller-method 存入容器
    private void initHandlerMappings(WApplicationContext context) {

        //1，找到所有requestMapping注解的类，方法
        //2、拼接Url地址

        try {
            String[] beanNames = context.getBeanDefinitions();
            for (String beanName : beanNames) {

                //这里getBean以后，是获取的代理对象
                Object aspectInstance = context.getBean(beanName);
                Object controller = WAopProxyUtils.getTargetObject(aspectInstance);
                Class<?> clazz = controller.getClass();

                if (!clazz.isAnnotationPresent(WController.class)) {
                    continue;
                }

                String baseUrl = "";
                if (clazz.isAnnotationPresent(WRequestMapping.class)) {
                    WRequestMapping wRequestMapping = clazz.getAnnotation(WRequestMapping.class);
                    baseUrl = wRequestMapping.value();
                }

                Method[] methods = clazz.getMethods();
                for (Method m : methods) {
                    if (!m.isAnnotationPresent(WRequestMapping.class)) {
                        continue;
                    }
                    WRequestMapping requestMapping = m.getAnnotation(WRequestMapping.class);

                    String regex = ("/" + baseUrl + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
                    WHandleMapping handleMapping = new WHandleMapping(controller, m, pattern);
                    this.handleMappings.add(handleMapping);

                    System.out.println("Mapping: " + regex + " , " + m);
                }
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    //初始化适配器 根据controller执行具体的method方法，反射，动态参数
    private void initHandlerAdapters(WApplicationContext context) {

        for (WHandleMapping handleMapping : handleMappings) {
            //参数索引位置，String 参数名称  Integer 参数所在形参中的具体位置，从0开始
            Map<String, Integer> indexMap = new HashMap<String, Integer>();

            //这里没有注解RequestParam的参数，有问题，spring 是通过asm的方式读取class字节码的方式处理的。
            //注解参数
            Annotation[][] annotations = handleMapping.getMethod().getParameterAnnotations();
            for (int i = 0; i < annotations.length; i++) {
                for (Annotation a : annotations[i]) {
                    //参数注解
                    if (a instanceof WRequestParam) {
                        String paramName = ((WRequestParam) a).value();
                        if (!paramName.trim().equals("")) {
                            indexMap.put(paramName, i);
                        }
                    }
                }
            }

            //这里的索引位置跟上面的索引位置是否会冲突？  已调试验证，不会
            //非注解参数
            Class<?>[] paramTypes = handleMapping.getMethod().getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                Class<?> paramType = paramTypes[i];
                if (paramType == HttpServletRequest.class || paramType == HttpServletResponse.class) {
                    indexMap.put(paramType.getName(), i);
                }
            }

            WHandleAdapter adapter = new WHandleAdapter(indexMap);
            this.handleAdapterMap.put(handleMapping, adapter);
        }


    }

    //初始化视图模版,把所有的页面初始化到容器中
    private void initViewResolvers(WApplicationContext context) {
        //View视图配置的路径
        String templateRoot = context.getConfig().getProperty("templateRoot");
        //获取类的程序位置：target/WEB-INF/classes
        File f = new File(this.getClass().getResource("/").getPath());
        //主目录位置target/
        String path = f.getParentFile().getParent();
        //获取文件路径
        String templateRootPath = path + "\\" + templateRoot;

        //获取所有视图文件
        File templateRootDir = new File(templateRootPath);
        for (File template : templateRootDir.listFiles()) {
            this.viewResolvers.add(new WViewResolver(template.getName(), template));
        }
    }


    private void initRequestToViewNameTranslator(WApplicationContext context) {

    }

    private void initHandlerExceptionResolvers(WApplicationContext context) {

    }

    private void initThemeResolver(WApplicationContext context) {

    }

    private void initLocaleResolver(WApplicationContext context) {

    }

    private void initMultipartResolver(WApplicationContext context) {

    }

    private void initFlashMapManager(WApplicationContext context) {

    }

}
